<template>
  <div class="fx-carousel" ref="carouselDom" :style="carouselStyle" @mouseenter="clearTimer"
       @mouseleave="initCarousel('to-left')">
    <div class="fx-carousel-inner" ref="carouselInnerDom">
      <slot></slot>
    </div>
    <div class="fx-carousel-path" :class="[arrow]" v-if="arrow!=='never'" @click="pathClick('to-right')">
      <i class="iconfont icon-left fs10"/>
    </div>
    <div class="fx-carousel-path right" :class="[arrow]" v-if="arrow!=='never'" @click="pathClick('to-left')">
      <i class="iconfont icon-right fs10"/>
    </div>
    <ul class="fx-carousel-dots" :class="[arrow,`fx-carousel-dots-${indicatorPosition}`]"
        v-if="isIndicator&&arrow!=='never'">
      <li v-for="(item,index) in currentChildren" :key="item"
          :class="{active:index===currentIndex}" @click="dotsClick(index)">
      </li>
    </ul>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent, onBeforeUnmount, onMounted, PropType, provide, ref, watch } from 'vue'
import { codeRule } from '@/base/types'

export type CarouselPath = 'to-left' | 'to-right'
type IndicatorPositionType = 'left' | 'right' | 'top' | 'bottom'

interface CarouselProps {
  index: number;
  width: string | number;
  height: string | number;
  autoPlay: boolean;
  interval: number;
  isIndicator: boolean;
  indicatorAnimation: boolean;
  indicatorPosition: IndicatorPositionType;
  loop: boolean;
  arrow: 'always' | 'hover' | 'never';
}

export default defineComponent({
  name: 'FCarousel',
  props: {
    // 开始页
    index: {
      type: Number as PropType<number>,
      default: 0,
      validator (val: number): boolean {
        return val >= 0 && Number.isInteger(val)
      }
    },
    // 轮播图高度
    width: {
      type: [String, Number] as PropType<string | number>,
      default: '100%'
    },
    // 轮播图高度
    height: {
      type: [String, Number] as PropType<string | number>,
      required: true
    },
    // 是否自动播放
    autoPlay: {
      type: Boolean as PropType<boolean>,
      default: true
    },
    // 自动播放速度
    interval: {
      type: Number as PropType<number>,
      default: 3000
    },
    // 是否需要指示器
    isIndicator: {
      type: Boolean as PropType<boolean>,
      default: true
    },
    // 是否需要指示器动画
    indicatorAnimation: {
      type: Boolean as PropType<boolean>,
      default: true
    },
    // 指示器位置
    indicatorPosition: {
      type: String as PropType<IndicatorPositionType>,
      default: 'bottom',
      validator (val: string) {
        return ['left', 'right', 'top', 'bottom'].includes(val)
      }
    },
    // 控制器显示方式
    arrow: {
      type: String as PropType<'always' | 'hover' | 'never'>,
      default: 'always',
      validator (val: string) {
        return ['always', 'hover', 'never'].includes(val)
      }
    },
    // 是否循环
    loop: {
      type: Boolean as PropType<boolean>,
      default: true
    }
  },
  emits: ['update:index', 'change'],
  setup (props: CarouselProps) {
    // 当前索引
    const currentIndex = ref<number>(props.index < 0 ? 0 : Math.floor(props.index))
    // 当前轮播图dom
    const currentChildren = ref<HTMLElement[]>([])
    // 轮播图内部dom
    const carouselInnerDom = ref<HTMLElement>()
    // 定时器
    const carouseTimer = ref<ReturnType<typeof setInterval> | null>(null)
    // 当前轮播方向
    const currentPath = ref<CarouselPath>('to-left')
    // 设定轮播图高度
    const carouselStyle = computed(() => {
      const isString = typeof props.height === 'string' && codeRule.test(props.height)
      const width = isString ? props.width : `${props.width}px`
      const height = isString ? props.height : `${props.height}px`
      return {
        width,
        height
      }
    })

    // 推送轮播图当前索引
    provide('carouselCurrentIndex', computed(() => currentIndex.value))
    // 推送轮播图当前方向
    provide('carouselCurrentPath', computed(() => currentPath.value))

    // 清空定时器
    const clearTimer = () => {
      carouseTimer.value && clearInterval(carouseTimer.value)
      carouseTimer.value = null
    }
    // 自动播放
    const autoPlayer = () => {
      clearTimer()
      if (props.autoPlay) {
        carouseTimer.value = setInterval(() => {
          pathClick('to-left')
        }, props.interval)
      }
    }
    // 监听自动播放事件
    watch(() => props.autoPlay, (val: boolean) => {
      if (val) autoPlayer()
    })
    // 初始化轮播图
    const initCarousel = (path: CarouselPath = currentPath.value) => {
      if (!carouselInnerDom.value) return
      currentPath.value = path
      const children = Array.from(carouselInnerDom.value?.children) as HTMLElement[]
      currentChildren.value = children
      if (children?.length) {
        children.forEach((item: HTMLElement, index: number) => {
          item.setAttribute('index', String(index))
        })
      }
      if (children.length <= 1) return
      if (currentIndex.value > children.length - 1) {
        currentIndex.value = props.loop ? 0 : children.length - 1
      }
      if (currentIndex.value < 0) {
        currentIndex.value = props.loop ? children.length - 1 : 0
      }
      if (!props.loop) clearTimer()
      autoPlayer()
    }
    // 轮播点击
    const pathClick = (path: CarouselPath) => {
      clearTimer()
      if (path === 'to-left') currentIndex.value++
      if (path === 'to-right') currentIndex.value--
      initCarousel(path)
    }
    // 轮播点点击
    const dotsClick = (index: number): void => {
      currentIndex.value = index
      initCarousel(index > currentIndex.value ? 'to-left' : 'to-right')
    }

    onMounted(() => {
      initCarousel()
    })
    onBeforeUnmount(() => {
      clearTimer()
    })

    return {
      currentIndex,
      carouselInnerDom,
      carouseTimer,
      carouselStyle,
      currentChildren,
      initCarousel,
      pathClick,
      clearTimer,
      dotsClick
    }
  }
})
</script>
